package com.chuangke.common.express;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.chuangke.common.exception.ChuangkeException;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.Options;

/**
 * 默认是用BigDecimal计算，传入的env必须是BigDecimal类型
 *
 * @author Administrator
 */
public class CkEvaluator {

    private static final Logger logger = LoggerFactory.getLogger(CkEvaluator.class);

    static {
        AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
    }

    public static List<String> getVariableNames(String expression) {
        return AviatorEvaluator.compile(expression).getVariableNames();
    }

    public static List<String> getVariableNames(String expression, boolean cached) {
        return AviatorEvaluator.compile(expression, cached).getVariableNames();
    }

    private int maxLevel = 1;

    public Map<String, Object> execute(List<CkExpression> expressionList, Map<String, Object> env) {
        Map<String, CkExpression> expressionMap = expressionList.stream()
                .collect(Collectors.toMap(CkExpression::getReferVar, a -> a));
        return execute(expressionMap, env, true);
    }

    public Map<String, Object> execute(List<CkExpression> expressionList, Map<String, Object> env, boolean cached) {
        Map<String, CkExpression> expressionMap = expressionList.stream()
                .collect(Collectors.toMap(CkExpression::getReferVar, a -> a));
        return execute(expressionMap, env, cached);
    }

    private Map<String, Object> execute(Map<String, CkExpression> expressions, Map<String, Object> env1,
                                        boolean cached) {
        Map<String, Object> resultMap = new ConcurrentHashMap<>();
        Map<String, Object> env = new ConcurrentHashMap<>(env1);

        logger.info("开始公式分级");
        Collection<CkExpression> parsedExpressions = parse(expressions);
        logger.info("完成公式分级");

        logger.info("开始分级计算");
        Map<Object, List<CkExpression>> leveExpressionMap = parsedExpressions.stream()
                .collect(Collectors.groupingBy(CkExpression::getLevel));

        for (int i = maxLevel; i >= 1; i--) {
            Map<String, Object> calResultMap = new ConcurrentHashMap<>();
            leveExpressionMap.get(i).parallelStream().forEach(expression -> {
                Map<String, Object> calEnv = new HashMap<>(env);//避免并发脏数据
                Object value = AviatorEvaluator.execute(expression.getExpression(), calEnv, cached);
                calResultMap.put(expression.getReferVar(), value);
            });
            env.putAll(calResultMap);
            resultMap.putAll(calResultMap);
        }

        logger.info("完成分级计算");
        return resultMap;
    }

    public Object execute(String expression, Map<String, Object> env) {
        return AviatorEvaluator.execute(expression, env, true);
    }

    public Object execute(String expression, Map<String, Object> env, boolean cached) {
        return AviatorEvaluator.execute(expression, env, cached);
    }

    public Collection<CkExpression> parse(List<CkExpression> expressionList) {
        Map<String, CkExpression> expressionMap = expressionList.stream()
                .collect(Collectors.toMap(CkExpression::getReferVar, a -> a));
        return parse(expressionMap);
    }

    private Collection<CkExpression> parse(Map<String, CkExpression> expressionMap) {
        if (expressionMap == null || expressionMap.isEmpty()) {
            return new ArrayList<>();
        }

        /* 遍历一级 */
//		for (CkExpression source : expressionMap.values()) {
//			parseFactor(source, expressionMap);
//		}
        expressionMap.values().parallelStream().forEach(source -> parseFactor(source, expressionMap));

        return expressionMap.values();
    }

    /**
     * @param dest
     */
    private void parseFactor(CkExpression dest, Map<String, CkExpression> expressionMap) {
        Expression expression = AviatorEvaluator.compile(dest.getExpression());
        List<String> varNames = expression.getVariableNames();

        for (String varName : varNames) {
            if (!expressionMap.containsKey(varName)) {
                continue;
            }

            CkExpression factor = expressionMap.get(varName);
            resetLevel(dest, factor);
            if (!dest.getFactorVars().contains(factor)) {
                dest.addFactorVar(factor);

                validate(dest);

                parseFactor(factor, expressionMap);
            }
        }
    }

    /**
     * 校验是否有循环引用的现象
     *
     * @param expression
     */
    private void validate(CkExpression expression) {
        List<CkExpression> container = new ArrayList<>();
        container.add(expression);
        validate(expression, container);
    }

    private void validate(CkExpression expression, List<CkExpression> container) {
        for (CkExpression factor : expression.getFactorVars()) {
            if (container.contains(factor)) {
                throw new ChuangkeException("公式存在循环引用：" + factor + "->" + expression);
            }
            container.add(factor);
            validate(factor, container);
        }
    }

    private void resetLevel(CkExpression father, CkExpression son) {
        if (son.getLevel() > (father.getLevel() + 1)) {
            return;
        }

        son.setLevel(father.getLevel() + 1);
        maxLevel = Math.max(maxLevel, son.getLevel());
        for (CkExpression grandSon : son.getFactorVars()) {
            resetLevel(son, grandSon);
        }
    }

    public int getMaxLevel() {
        return maxLevel;
    }

}
